package tech.blueglacier.email;

import org.apache.james.mime4j.dom.field.ContentTypeField;
import org.apache.james.mime4j.dom.field.FieldName;
import org.apache.james.mime4j.dom.field.ParsedField;
import org.apache.james.mime4j.message.MaximalBodyDescriptor;
import tech.blueglacier.storage.AbstractStorageProvider;
import tech.blueglacier.storage.MemoryStorageProvider;
import org.apache.james.mime4j.storage.Storage;
import org.apache.james.mime4j.stream.BodyDescriptor;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;

public abstract class Attachment {

    private static final String MULTIPART_RELATED_CONTENT_TYPE = "multipart/related";

    private InputStream is;
    private Storage storage;
    private int attachmentSize;

    protected BodyDescriptor bd;

    private static Field bdField;

    static {
        try {
            bdField = MaximalBodyDescriptor.class.getDeclaredField("fields");
            bdField.setAccessible(true);
        } catch (NoSuchFieldException impossible) {
            // impossible
        }
    }

    public Attachment(BodyDescriptor bd, InputStream is) {
        this.bd = bd;
        attachmentSize = 0;
        setIs(is);
    }

    public abstract String getAttachmentName();

    public BodyDescriptor getBd() {
        return bd;
    }

    public int getAttachmentSize() {
        return attachmentSize;
    }

    public InputStream getIs() {
        try {
            is = storage.getInputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return is;
    }

    public void setIs(InputStream is) {
        AbstractStorageProvider storageProvider = new MemoryStorageProvider();
        try {
            storage = storageProvider.store(is);
            attachmentSize = storageProvider.getTotalBytesTransferred();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public boolean isAggregatedAttachment() {
        if (!(bd instanceof MaximalBodyDescriptor)) {
            return false;
        }

        try {
            Map<String, ParsedField> contentType = (Map<String, ParsedField>) bdField.get(bd);
            if (!contentType.containsKey(FieldName.CONTENT_TYPE.toLowerCase(Locale.US))) {
                return false;
            }

            ContentTypeField contentTypeField = (ContentTypeField) contentType.get(FieldName.CONTENT_TYPE.toLowerCase(Locale.US));
            return contentTypeField.getBody().contains(MULTIPART_RELATED_CONTENT_TYPE);
        } catch (IllegalAccessException e) {
            return false;
        }
    }
}